#ifndef SUDOKU_H
#define SUDOKU_H

#include "BigBlock.h"
#include <QList>
#include <QDebug>
#include <iostream>
#include <QFile>
#include <QDebug>
#include <QtXml/qdom.h>
#include <QList>
#include <QVector>

using namespace std ;


class Sudoku
{
public:
    //Main Constructor
    Sudoku() {}
    ~Sudoku() ;



    //Get number of all cells
    int getAllElements() ;
    //Get no empty cells
    int getBusyElements() ;
    //Read sudoku from XML element

    //Sudoku
    void print( std::ostream& out ) ;
    //Print each cell in detail
    void printFull( ostream& out ) ;

    //Add element to actual transaction
    static void addPartitionElement( PartitionElement* newPartitionElement )
    {
        newPartitionElement->setTransactionID( transactionCounter );
        partitionVector.append( newPartitionElement );
    }

    //Create new transaction
    int initTransaction() ;
    //Rollback las transaction
    void clearLastTransaction() ;

    //Test sudoku on deadlock
    bool isDeadlock() ;

    //Set assumptions
    int service() ;


    //Check sudoku on rules
    //True if all is good
    bool checkSudoku() ;

    bool allIsFilled() ;
    bool assumptionsIs() ;

    PartitionElement* makeAssumption() ;

protected :


private :
    //-----Fields-----

    //List of BibBlocks
    QList<BigBlock*> bigBlockList ;
    //Matrix with small block (need for output and other)
    SmallBlock*** matrix ;
    //Range of matrix
    int xMax ;
    int yMax ;

    static int transactionCounter ;
    static QVector<PartitionElement*> partitionVector ;

    //---Inner Functions---
    QList<BigBlock*>* smallBlockToBigBlock() ;

public:
    static Sudoku* readSudoku( QDomElement root )
    {
        QList<SmallBlock*> smallBlockList ;

        QDomNode node = root.firstChild() ;

        while( !node.isNull() ) {
            if( node.isElement() ) {
                QDomElement element = node.toElement() ;
                int x = element.attribute( "x" ).toInt() ;
                int y = element.attribute( "y" ).toInt() ;

                QDomNode tempNode = node.firstChild() ;

                short* smallBlock = new short[9] ;   //Array with elements
                for( int i=0 ; i<9 && !tempNode.isNull() ; ++i ) {
                    QDomElement element = tempNode.toElement() ;
                    QString str = element.text() ;
                    smallBlock[i] = str.toShort() ;
                    tempNode = tempNode.nextSibling() ;
                }

                SmallBlock* block = new SmallBlock( x, y, smallBlock ) ;
                smallBlockList.append( block ) ;

                delete [] smallBlock ;
            }
            node = node.nextSibling() ;
        }

        Sudoku* sudoku = new Sudoku() ;

        //Calculate size of matrix with small elements
        sudoku->xMax = -1 ;
        sudoku->yMax = -1 ;
        foreach( SmallBlock* ptr, smallBlockList )
        {
            sudoku->xMax = ( ptr->getX()>sudoku->xMax ) ? ptr->getX() : sudoku->xMax ;
            sudoku->yMax = ( ptr->getY()>sudoku->yMax ) ? ptr->getY() : sudoku->yMax ;
        }
        sudoku->xMax++ ;
        sudoku->yMax++ ;

        //Create matrix for 'small elements'
        sudoku->matrix = new SmallBlock**[ sudoku->xMax ] ;
        for( int i=0 ; i<sudoku->xMax ; ++i )
        {
            sudoku->matrix[i] = new SmallBlock*[ sudoku->yMax ] ;
        }

        //Initalize matrix for small elements
        for( int row=0 ; row<sudoku->xMax ; ++row )
        {
            for( int column=0 ; column<sudoku->yMax ; ++column )
            {
                sudoku->matrix[ row ][ column ] = 0 ;
            }
        }

        //Fill matrix with small elements
        foreach( SmallBlock* block, smallBlockList )
        {
            int x = block->getX() ;
            int y = block->getY() ;
            sudoku->matrix[ x ][ y ] = block ;
        }

        QList<BigBlock*>* temp = sudoku->smallBlockToBigBlock();
        sudoku->bigBlockList = *temp ;
        delete temp ;
        return sudoku ;
    }
} ;

#endif // SUDOKU_H
